Skip to content

service: the daemon needs more than launchd's default descriptors - #78

Merged
myobie merged 1 commit into
mainfrom
service/daemon-needs-more-than-256-descriptors
Aug 25, 2026
Merged

service: the daemon needs more than launchd's default descriptors#78
myobie merged 1 commit into
mainfrom
service/daemon-needs-more-than-256-descriptors

Conversation

@myobie

@myobie myobie commented Aug 25, 2026

Copy link
Copy Markdown
Collaborator

The Mac daemon died with Error: Too many open files (os error 24) — it's in service.err.log on Silber.

Why

render_launch_agent_plist writes SoftResourceLimits only when the operator passes --memory-max-mb, and even then only ResidentSetSize. NumberOfFiles appears nowhere, so a fabric agent inherits launchd's own default, which is small and which nobody chose for this process.

The daemon holds a QUIC endpoint, a connection per peer, a control socket, a dial socket per tunnel, and the files it is syncing.

What changed

A descriptor ceiling is not optional, so it is now always written. The resident-set ceiling stays opt-in and unchanged — launchd treats ResidentSetSize as a reclaim preference rather than a kill, but it is still a fixed number, and shipping one by default declares a healthy working set nobody has measured.

8192 is not a measurement, and saying so matters. It is far above any working set this daemon has shown — the entry syncing 17,600 files sat at a few dozen descriptors. The point is to remove an arbitrary small number, not to install a different arbitrary number close enough to matter.

A check that the plist is actually a plist

A malformed plist doesn't fail a string assertion — it fails at install time on somebody's machine. rendered_plists_are_valid_property_lists renders both branches and hands them to plutil -lint.

I proved it can fail by injecting an unclosed tag:

plutil rejected the plist for memory_max_mb=None:
(Encountered unknown tag unclosed on line 31)

Off a Mac there's no plutil, so it reports that the plist was not linted rather than passing silently as though it had been.

Not applied to the running service

The plist on disk is the one the old build wrote; replacing it takes a fabric service install. Machine runtime belongs to root, so I'll tell Silber.root before touching the launchd job.

Agent: Silber.fabric

The Mac daemon died with `Error: Too many open files (os error 24)`. It is
in `service.err.log` on Silber.

WHY. `render_launch_agent_plist` writes `SoftResourceLimits` ONLY when the
operator passes `--memory-max-mb`, and even then it writes only
`ResidentSetSize`. `NumberOfFiles` appears nowhere. So a fabric agent
inherits launchd's own default, which is small, and nobody chose it.

That default is not a ceiling anybody picked for this process. The daemon
holds a QUIC endpoint, a connection per peer, a control socket, a dial
socket per tunnel, and the files it is syncing.

A DESCRIPTOR CEILING IS NOT OPTIONAL, so it is now always written. The
resident-set ceiling stays opt-in and unchanged: launchd treats
ResidentSetSize as a reclaim preference rather than a kill, but it is
still a fixed number, and shipping one by default declares a healthy
working set nobody has measured.

8192 IS NOT A MEASUREMENT AND I AM SAYING SO. It is far above any working
set this daemon has shown; the entry syncing 17,600 files sat at a few
dozen descriptors. The point is to remove an arbitrary small number, not
to install a different arbitrary number close enough to matter.

I ALSO ADDED A CHECK THAT THE PLIST IS A PLIST. A malformed one does not
fail a string assertion, it fails at install time on somebody's machine.
`rendered_plists_are_valid_property_lists` renders both branches and hands
them to `plutil -lint`. I proved it can fail by injecting an unclosed tag:

  plutil rejected the plist for memory_max_mb=None:
  (Encountered unknown tag unclosed on line 31)

Off a Mac there is no `plutil`, so it says the plist was not linted rather
than passing silently as though it had been.

NOT APPLIED TO THE RUNNING SERVICE YET. The plist on disk is the one the
old build wrote; it takes a `fabric service install` to replace it, and
machine runtime belongs to root, so I will tell `Silber.root` before
touching the launchd job.

Agent: Silber.fabric
@myobie
myobie merged commit 77dad12 into main Aug 25, 2026
2 checks passed
@myobie
myobie deleted the service/daemon-needs-more-than-256-descriptors branch August 25, 2026 07:32
myobie added a commit that referenced this pull request Aug 25, 2026
#78 gave the launchd plist a `NumberOfFiles` because the Mac daemon had
died of `Too many open files`. The systemd unit has the same omission and
I only fixed one side, which is how a defect keeps a door open.

`LimitNOFILE` appeared nowhere in `render_systemd_user_unit`, so a Linux
agent inherits whatever the user manager defaults to.

MEASURED ON BOTH PEERS RATHER THAN ASSUMED. Linux is not in the same
trouble the Mac was, and that is worth stating plainly:

  hetz     soft 1024, hard 524288, 53 descriptors in use
  droppy   soft 1024, hard 524288, 27 descriptors in use

1024 is four times launchd's default and neither peer has come close to
it. So this is not a fire. It is the same inherited-number problem, and
1024 is still a ceiling nobody picked for a daemon that holds a QUIC
endpoint, a connection per peer, a control socket and a dial socket per
tunnel.

8192 matches the plist, and it is not a measurement there either. Both
platforms now state the ceiling instead of inheriting one.

Agent: Silber.fabric
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant